﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace FinMkts.DataProvider.Internal.UnitTests
{
    [TestClass]
    public class InternalPriceSerializerTests
    {
        private readonly InternalPriceSerializer _serializer = new InternalPriceSerializer();

        [TestMethod, ExpectedException(typeof(ArgumentNullException))]
        public void cannot_serialize_null_message()
        {
            _serializer.Serialize(null);
        }

        [TestMethod, ExpectedException(typeof(ArgumentException))]
        public void cannot_serialize_null_listingCode()
        {
            _serializer.Serialize(new InternalPrice { ListingCode = null});
        }
        
        [TestMethod, ExpectedException(typeof(ArgumentException))]
        public void cannot_serialize_empty_ListingCode()
        {
            _serializer.Serialize(new InternalPrice { ListingCode = string.Empty });
        }

        [TestMethod, ExpectedException(typeof(ArgumentException))]
        public void cannot_serialize_blank_ListingCode()
        {
            _serializer.Serialize(new InternalPrice { ListingCode = "  " });
        }

        [TestMethod]
        public void can_serialize_populated_price()
        {
            var price = new InternalPrice
                            {
                                ListingCode = "test",
                                Bid = 1,
                                Ask = 2,
                                LastTradePrice = 1.5,
                            };
            var serialized = _serializer.Serialize(price);
            Assert.IsNotNull(serialized, "null returned");
            Assert.IsTrue(serialized.Length > 0, "zero bytes returned");
        }

        [TestMethod, ExpectedException(typeof(ArgumentNullException))]
        public void cannot_deserialize_null()
        {
            _serializer.Deserialize(null);
        }

        [TestMethod]
        public void can_deserialize_empty_array()
        {
            var actual = _serializer.Deserialize(new byte[0]);
            CheckDeserialized(new InternalPrice(), actual);
            Assert.AreEqual(0, actual.Errors.Count, "Errors");
            Assert.AreEqual(0, actual.ExtraData.Count, "ExtraData");
        }
  
        private void CheckDeserialized(InternalPrice original, InternalPrice actual)
        {
            Assert.IsNotNull(actual);
            Assert.AreEqual(original.ListingCode, actual.ListingCode, "ListingCode");
            Assert.AreEqual(original.Bid, actual.Bid, "Bid");
            Assert.AreEqual(original.Ask, actual.Ask, "Ask");
            Assert.AreEqual(original.LastTradePrice, actual.LastTradePrice, "LastTradePrice");
            Assert.IsNotNull(actual.Errors, "Errors is null");
            Assert.IsNotNull(actual.ExtraData, "ExtraData is null");
        }

        [TestMethod]
        public void can_deserialize_populated_price()
        {
            var original = new InternalPrice
            {
                ListingCode = "test",
                Bid = 1,
                Ask = 2,
                LastTradePrice = 1.5,
            };
            var serialized = _serializer.Serialize(original);
            Assert.IsNotNull(serialized, "null returned");
            Assert.IsTrue(serialized.Length > 0, "zero bytes returned");

            var actual = _serializer.Deserialize(serialized);
            CheckDeserialized(original, actual);
            Assert.AreEqual(0, actual.Errors.Count, "Errors");
            Assert.AreEqual(0, actual.ExtraData.Count, "ExtraData");
        }
        
        [TestMethod]
        public void can_deserialize_extra_price_data()
        {
            string csv = "test,1,2,1.5,this,that";
            var serialized = Encoding.UTF8.GetBytes(csv);
            var actual = _serializer.Deserialize(serialized);
            Assert.AreEqual(0, actual.Errors.Count, "Errors");
            Assert.AreEqual(2, actual.ExtraData.Count, "ExtraData");
            Assert.AreEqual("this", actual.ExtraData.ElementAt(0), "ExtraData[0]");
            Assert.AreEqual("that", actual.ExtraData.ElementAt(1), "ExtraData[1]");
        }

        [TestMethod]
        public void can_deserialize_with_bid_error()
        {
            string csv = "test,a,2,1.5";
            var serialized = Encoding.UTF8.GetBytes(csv);
            var actual = _serializer.Deserialize(serialized);

            Assert.IsNotNull(actual);
            Assert.AreEqual("test", actual.ListingCode, "ListingCode");
            //Assert.AreEqual(original.Bid, actual.Bid, "Bid");
            Assert.IsNull(actual.Bid, "Expected bid to be null");
            Assert.AreEqual(2.0, actual.Ask, "Ask");
            Assert.AreEqual(1.5, actual.LastTradePrice, "LastTradePrice");
            Assert.IsNotNull(actual.Errors, "Errors is null");
            Assert.IsNotNull(actual.ExtraData, "ExtraData is null");
            Assert.AreEqual(1, actual.Errors.Count, "Errors");
            Assert.AreEqual(0, actual.ExtraData.Count, "ExtraData");
            Assert.AreEqual("Bid is invalid: a", actual.Errors.ElementAt(0), "Bid error");
        }

        [TestMethod]
        public void can_deserialize_with_ask_error()
        {
            string csv = "test,1,b,1.5";
            var serialized = Encoding.UTF8.GetBytes(csv);
            var actual = _serializer.Deserialize(serialized);
            Assert.IsNotNull(actual);
            Assert.AreEqual("test", actual.ListingCode, "ListingCode");
            Assert.AreEqual(1.0, actual.Bid, "Bid");
            //Assert.AreEqual(2.0, actual.Ask, "Ask");
            Assert.IsNull(actual.Ask, "Expected bid to be null");
            Assert.AreEqual(1.5, actual.LastTradePrice, "LastTradePrice");
            Assert.IsNotNull(actual.Errors, "Errors is null");
            Assert.IsNotNull(actual.ExtraData, "ExtraData is null");
            Assert.AreEqual(1, actual.Errors.Count, "Errors");
            Assert.AreEqual(0, actual.ExtraData.Count, "ExtraData");
            Assert.AreEqual("Ask is invalid: b", actual.Errors.ElementAt(0), "Ask error");
        }

        [TestMethod]
        public void can_deserialize_with_lastPrice_error()
        {
            string csv = "test,1,2,c";
            var serialized = Encoding.UTF8.GetBytes(csv);
            var actual = _serializer.Deserialize(serialized);
            Assert.IsNotNull(actual);
            Assert.AreEqual("test", actual.ListingCode, "ListingCode");
            Assert.AreEqual(1.0, actual.Bid, "Bid");
            Assert.AreEqual(2.0, actual.Ask, "Ask");
            //Assert.AreEqual(1.5, actual.LastTradePrice, "LastTradePrice");
            Assert.IsNull(actual.LastTradePrice, "Expected LastTradePrice to be null");
            Assert.IsNotNull(actual.Errors, "Errors is null");
            Assert.IsNotNull(actual.ExtraData, "ExtraData is null");
            Assert.AreEqual(1, actual.Errors.Count, "Errors");
            Assert.AreEqual(0, actual.ExtraData.Count, "ExtraData");
            Assert.AreEqual("LastTradePrice is invalid: c", actual.Errors.ElementAt(0), "LastTradePrice error");
        }

    }
}
